<?php
/*
 * ML 框架入口文件：ml.php
 * auth: Mirze 2013-05-28
 * 
 */
//DIRECTORY_SEPARATOR 的简写
define('DS', DIRECTORY_SEPARATOR);
//CURRENT_TIMESTAMP 定义为当前时间，减少框架调用 time() 的次数
define('CURRENT_TIMESTAMP', time());

//ML 框架路径
define('ML_PATH', dirname(__FILE__));
//library路径
define('LIBRARY_PATH', ML_PATH . DS . 'library');

global $G_CLASS_FILES;
if (empty($G_CLASS_FILES)) {
    require ML_PATH . '/conf/ml_class_files.php';
}

class ML {
	//类搜索路径
    private static $_class_path = array();
    //类搜索路径的选项
    private static $_class_path_options = array();
    //应用程序设置
    private static $_ini = array();


	/**
     * 返回 MLPHP 版本号
     */
    static function version() {
        return '0.1';
    }

    /**
     * 注册或取消注册一个自动类载入方法
     *
     * @param string $class 提供自动载入服务的类
     * @param boolean $enabled 启用或禁用该服务
     */
    static function registerAutoload($class = 'ML', $enabled = true) {
        if (!function_exists('spl_autoload_register')) {
            $error = 'spl_autoload does not exist in this PHP installation';
            throw new Exception($error);
        }

        if ($enabled === true) {
            spl_autoload_register(array($class, 'autoload'));
        } else {
            spl_autoload_unregister(array($class, 'autoload'));
        }
    }

    /**
     * 用于类自动载入，不需要由开发者调用
     *
     * @param string $class_name
     */
    static function autoload($class_name) {
        self::loadClass($class_name, null, false);
    }

    /**
     * 载入指定类的定义文件，如果载入失败抛出异常
     *
     * @code php
     * ML::loadClass('Table_Posts');
     * @endcode
     *
     * $dirs 参数可以是一个以 PATH_SEPARATOR 常量分隔的字符串，
     * 也可以是一个包含多个目录名的数组。
     *
     * @code php
     * ML::loadClass('Table_Posts', array('/www/mysite/app', '/www/mysite/lib'));
     * @endcode
     *
     * @param string $class_name 要载入的类
     * @param string|array $dirs 指定载入类的搜索路径
     *
     * @return string|boolean 成功返回类名，失败返回 false
     */
    static function loadClass($class_name, $dirs = null, $throw = true) {
        if (class_exists($class_name, false) || interface_exists($class_name, false)) {
            return $class_name;
        }

        global $G_CLASS_FILES;
        $class_name_l = strtolower($class_name);
        if (isset($G_CLASS_FILES[$class_name_l])) {
            require LIBRARY_PATH . DS . $G_CLASS_FILES[$class_name_l];
            return $class_name_l;
        }

        $filename = str_replace('_', DS, $class_name);
        if ($filename != $class_name) {
            $dirname = dirname($filename);
            if (!empty($dirs)) {
	            if (!is_array($dirs)) {
                    $dirs = explode(PATH_SEPARATOR, $dirs);
                }
            } else {
                $dirs = self::$_class_path;
            }
            $filename = basename($filename) . '.class.php';
            return self::loadClassFile($filename, $dirs, $class_name, $dirname, $throw);
        } else {
            return self::loadClassFile("{$filename}.class.php", self::$_class_path, $class_name, '', $throw);
        }
    }

    /**
     * 载入特定文件，并检查是否包含指定类的定义
     *
     * 该方法从 $dirs 参数提供的目录中查找并载入 $filename 参数指定的文件。
     * 然后检查该文件是否定义了 $class_name 参数指定的类。
     *
     * 如果没有找到指定类，则抛出异常。
     *
     * @code php
     * ML::loadClassFile('Smarty.class.php', $dirs, 'Smarty');
     * @endcode
     *
     * @param string $filename 要载入文件的文件名（含扩展名）
     * @param string|array $dirs 文件的搜索路径
     * @param string $class_name 要检查的类
     * @param string $dirname 是否在查找文件时添加目录前缀
     * @param string $throw 是否在找不到类时抛出异常
     */
    static function loadClassFile($filename, $dirs, $class_name, $dirname = '', $throw = true)
    {
        if (!is_array($dirs)) {
            $dirs = explode(PATH_SEPARATOR, $dirs);
        }
        if ($dirname) {
            $filename = rtrim($dirname, '/\\') . DS . $filename;
        }
        $filename_l = strtolower($filename);

        foreach ($dirs as $dir) {
            if (isset(self::$_class_path[$dir])) {
                $path = $dir . DS . (self::$_class_path_options[$dir] ? $filename : $filename_l);
            } else {
                $path = rtrim($dir, '/\\') . DS . $filename;
            }

            if (is_file($path)) {
                require $path;
                break;
            }
        }

        // 载入文件后判断指定的类或接口是否已经定义
        if (!class_exists($class_name, false) && ! interface_exists($class_name, false)) {
            if ($throw) {
            	$error = sprintf('Class "%s" not defined in file "%s".', $class_name, $filename);
            	throw new Exception($error);
            }
            return false;
        }
        return $class_name;
    }

    /*
     * 添加一个类搜索路径: 添加的路径和类名称有关系
     * 如果要使用 ML::loadClass() 载入非 MLPHP 的类，需要通过 ML::import() 添加类类搜索路径。
     *
     * @code php
     * ML::import('/www/app');
     * ML::loadClass('Vendor_Smarty_Adapter');
     * // 实际载入的文件是 /www/app/vendor/smarty/adapter.php
     * @endcode
     *
     * @param string $dir 要添加的搜索路径
     * @param boolean $case_sensitive 在该路径中查找类文件时是否区分文件名大小写
     */
    static function import($dir, $case_sensitive = false) {
        $real_dir = realpath($dir);
        if ($real_dir) {
            $dir = rtrim($real_dir, '/\\');
            if (!isset(self::$_class_path[$dir])) {
                self::$_class_path[$dir] = $dir;
                self::$_class_path_options[$dir] = $case_sensitive;
            }
        }
    }

    /**
     * 获取指定的设置内容
     *
     * $option 参数指定要获取的设置名。
     * 如果设置中找不到指定的选项，则返回由 $default 参数指定的值。
     *
     * @code php
     * $option_value = ML::ini('my_option');
     * @endcode
     *
     * 对于层次化的设置信息，可以通过在 $option 中使用“/”符号来指定。
     *
     * 例如有一个名为 option_group 的设置项，其中包含三个子项目。
     * 现在要查询其中的 my_option 设置项的内容。
     *
     * @code php
     * // +--- option_group
     * //   +-- my_option  = this is my_option
     * //   +-- my_option2 = this is my_option2
     * //   \-- my_option3 = this is my_option3
     *
     * // 查询 option_group 设置组里面的 my_option 项
     * // 将会显示 this is my_option
     * echo ML::ini('option_group/my_option');
     * @endcode
     *
     * 要读取更深层次的设置项，可以使用更多的“/”符号，但太多层次会导致读取速度变慢。
     *
     * 如果要获得所有设置项的内容，将 $option 参数指定为 '/' 即可：
     *
     * @code php
     * // 获取所有设置项的内容
     * $all = ML::ini('/');
     * @endcode
     *
     * @param string $option 要获取设置项的名称
     * @param mixed $default 当设置不存在时要返回的设置默认值
     *
     * @return mixed 返回设置项的值
     */
    static function ini($option, $default = null) {
        if ($option == '/') return self::$_ini;

        if (strpos($option, '/') === false) {
            return array_key_exists($option, self::$_ini) ? self::$_ini[$option] : $default;
        }

        $parts = explode('/', $option);
        $pos =& self::$_ini;
        foreach ($parts as $part) {
            if (!isset($pos[$part])) return $default;
            $pos =& $pos[$part];
        }
        return $pos;
    }

    /**
     * 替换已有的设置值:直接替换到指定的设置项及其子项目。
     *
     * @code php
     * // 假设已有的设置为
     * // +--- option_1 = old value
     * // +--- option_group
     * //   +-- option1 = old value
     * //   +-- option2 = old value
     * //   \-- option3 = old value
     *
     * // 替换多个设置项
     * $arr = array(
     *      'option_1' => 'value 1',
     *      'option_2' => 'value 2',
     *      'option_group/option2' => 'new value',
     * );
     * ML::replaceIni($arr);
     *
     * // 修改后
     * // +--- option_1 = value 1
     * // +--- option_2 = value 2
     * // +--- option_group
     * //   +-- option2 = new value
     * @endcode
     *
     * @param string|array $option 要修改的设置项名称，或包含多个设置项目的数组
     * @param mixed $data 指定设置项的新值
     */
    static function replaceIni($option, $data = null) {
        if (is_array($option)) {
            self::$_ini = array_merge(self::$_ini, $option);
        } else {
            self::$_ini[$option] = $data;
        }
    }



}

//设置对象的自动载入
ML::registerAutoload();

//加载 MLPHP 基础函数及公共函数
require_once(ML_PATH . DS . 'common' . DS.'common.php');
require_once(ML_PATH . DS . 'common' . DS.'functions.php');
